#pragma once

#include <iostream>
#include <vector>
#include <queue>
#include <pthread.h>
#include "task.hpp"
#include "Thread.hpp"
#include "lockGuard.hpp"

const int N = 5;

// 线程池的本质也是一个PC模型，由用户担任生产者进行推送任务到任务队列中，
// 再由线程池中的线程进行任务处理。
template <class T>
class threadPool
{
public:
    threadPool(int num = N)
        : _cap(N)
    {
        pthread_mutex_init(&_mtx, nullptr);
        pthread_cond_init(&_cond, nullptr);
    }

    void lock() { pthread_mutex_lock(&_mtx); }
    void unlock() { pthread_mutex_unlock(&_mtx); }
    void wait() { pthread_cond_wait(&_cond, &_mtx); }
    void wakeup() { pthread_cond_signal(&_cond); }
    bool isEmpty() { return _tasks.empty(); }
    pthread_mutex_t *getmutex() { return &_mtx; }

    T popTask()
    {
        // 拿取任务
        T t = _tasks.front();
        _tasks.pop();
        return t;
    }

    static void *threadRoutine(void *args)
    {
        // 让线程池中的线程分离，这样我们就不必关心join的问题了，让任务被处理完之后，线程就会结束。
        pthread_detach(pthread_self());

        threadPool<T> *tp = static_cast<threadPool<T> *>(args);

        while (true)
        {
            // 1. 检测有没有任务
            // 2. 有：处理
            // 3. 无：等待
            // 细节：必定加锁.任务是临界资源，对临界资源的访问必定要加锁,但是该函数是静态成员函数，无法直接访问非静态的成员变量
            // 这里的做法是，对需要访问的成员变量都提供一个接口

            T t;
            {
                LockGuard guard(tp->getmutex());
                if (tp->isEmpty())
                {
                    // 如果任务队列为空，就继续等待，什么都不做
                    tp->wait();
                }
            }

            t = tp->popTask();
            // 进行任务处理阶段
            t(); // 任务的处理方式，可以自己在封装任务的类中定制,这里任务处理的方式就是以运算符重载的方式进行

            std::cout << "thread handler done, result: " << t.formatRes() << std::endl;
        }
    }
    void init()
    {
        // 对线程池进行初始化处理
        for (int i = 0; i < _cap; ++i)
        {
            _threads.push_back(Thread(i, threadRoutine, this));
        }
    }
    void start()
    {
        for (auto &e : _threads)
        {
            e.run();
        }
    }
    void pushTask(const T &in)
    {
        // V3我们将加锁和解锁自动化---RAII
        LockGuard guard(&_mtx);
        _tasks.push(in);
        pthread_cond_signal(&_cond); // 当有任务来到时，唤醒条件变量让线程池中的线程对线程进行处理
    }

    ~threadPool()
    {
        for (auto &t : _threads)
        {
            t.join();
        }
        pthread_mutex_destroy(&_mtx);
        pthread_cond_destroy(&_cond);
    }

private:
    // V2版本使用自己封装的线程
    std::vector<Thread> _threads;
    int _cap; // 线程池的容量大小

    std::queue<T> _tasks; // 任务队列,任务队列就不设置大小了，使用stl的自动扩容

    pthread_mutex_t _mtx;
    pthread_cond_t _cond;
};